﻿using System;
using System.Drawing;
using System.Windows.Forms;
using HCSDK;
using System.Drawing.Imaging;
using System.Threading;
using System.Runtime.InteropServices;

namespace VideoControl
{
public partial class Video : UserControl
{

    #region Property
    public int VideoChannel { get { return videoChannel; } }
    //public int Videochannel { get; private set; }
    #endregion

    #region Parameter
    private bool InitializedForDevice = false;
    private HCSDK.NET_DVR_DEVICEINFO_V30 DvcInfo;//设备信息
    private int DvcUserID = -1;//登陆用户的Id
    private int videoChannel = -1;//视频通道
    private int DvcPlayHandle = -1;//播放句柄
    private HCSDK.NET_DVR_CLIENTINFO DvcCliInfo = new HCSDK.NET_DVR_CLIENTINFO();
    private HCSDK.HCNetSDK.RealDataCallBack_V30 DvcRealDataCallBackHandle;//解码回调
    private int DecodePort = -1;//解码的端口
    private int pWidth;//图像的宽度
    private int pHeight;//图像的高度

    private bool isRealPlaying = false;

    public bool IsRealPlaying
    {
        get { return isRealPlaying; }
    }
    #endregion

    #region Device Related Enum
    /// <summary>
    /// 打开设备的流类型
    /// </summary>
    public enum StreamType : int
    {
        Master = 0 >> 31,
        Slave = 1 >> 31
    };
    /// <summary>
    /// 连接到设备上去的类型
    /// </summary>
    public enum LinkMode : int
    {
        TCP = 0,
        UDP = 1,
        MultiCast = 2,
        RTP = 3,
        VA = 4
    };

    #endregion


    #region Constructor & Initialize
    public Video()
    {
        InitializeComponent();
    }
    /// <summary>
    /// 初始化设备
    /// </summary>
    public void InitializeForDevice()
    {
        InitializedForDevice = HCSDK.HCNetSDK.NET_DVR_Init();
    }
    #endregion

    private string deviceArea = null;

    public string DeviceArea
    {
        get { return deviceArea; }
        set { deviceArea = value; }
    }
    private string deviceName = null;

    public string DeviceName
    {
        get { return deviceName; }
        set { deviceName = value; }
    }
    protected byte deviceAddress = 0x00;
    public byte DeviceAddress
    {
        get { return deviceAddress; }
        set { deviceAddress = value; }
    }
    #region Login & Logout
    /// <summary>
    /// 登陆设备
    /// </summary>
    /// <param name="DVRIP">设备ip地址</param>
    /// <param name="DVRPort">设备的端口号</param>
    /// <param name="Username">登陆用户名</param>
    /// <param name="Password">登陆密码</param>
    /// <returns> true表示登陆成功，失败返回false</returns>
    public bool LoginToDevice(string DVRIP, ushort DVRPort, string Username, string Password)
    {
        InitializeForDevice();
        if (!InitializedForDevice)
            throw new InvalidOperationException();

        if (DvcUserID >= 0)
            throw new InvalidOperationException();

        DvcUserID = HCSDK.HCNetSDK.NET_DVR_Login_V30(DVRIP, DVRPort, Username, Password, out DvcInfo);
        return (DvcUserID >= 0);
    }
    /// <summary>
    /// 从设备上退出
    /// </summary>
    /// <returns></returns>
    public bool LogoutFromDevice()
    {
        if (DvcUserID < 0)
            throw new InvalidOperationException();

        if (HCSDK.HCNetSDK.NET_DVR_Logout_V30(DvcUserID))
        {
            DvcUserID = -1;
            this.Invalidate();
            this.deviceArea = null;
            this.deviceName = null;
            return true;
        }
        else
            return false;
    }



    #endregion

    #region Video
    public bool DvcOpenRealPlay()
    {
        return DvcOpenRealPlay(1, VideoControl.Video.StreamType.Master, VideoControl.Video.LinkMode.TCP);
    }
    /// <summary>
    /// 打开视频
    /// </summary>
    /// <param name="Channel">打开视频的通道</param>
    /// <param name="Stream">视频的流类型</param>
    /// <param name="Mode">连接方式</param>
    /// <returns>true表示成功，false表示失败</returns>
    public bool DvcOpenRealPlay(byte videoChannel, StreamType Stream, LinkMode Mode)
    {

        if (DvcUserID < 0)
            throw new InvalidOperationException();

        if (videoChannel <= 0 || videoChannel > DvcInfo.byChanNum)
            throw new ArgumentOutOfRangeException();

        DvcRealDataCallBackHandle = new HCSDK.HCNetSDK.RealDataCallBack_V30(DvcRealDataCallback);

        DvcCliInfo.hPlayWnd = this.Handle;
        DvcCliInfo.lChannel = videoChannel;
        DvcCliInfo.lLinkMode = ((int)Stream) | ((int)Mode);
        DvcPlayHandle = HCSDK.HCNetSDK.NET_DVR_RealPlay_V30(DvcUserID, ref DvcCliInfo, DvcRealDataCallBackHandle, IntPtr.Zero, true);
        this.isRealPlaying = true;
        if (DvcPlayHandle < 0)
            return false;
        else
        {
            //Default Close Sound
            HCSDK.HCNetSDK.NET_DVR_CloseSoundShare(DvcPlayHandle);
            this.videoChannel = videoChannel;

            OnOpen();

            return true;
        }
    }

    protected virtual void OnOpen()
    {

    }

    private void DvcRealDataCallback(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr pUser)
    {
        if (DvcPlayHandle < 0 || DvcPlayHandle != lRealHandle)
            return;

        //IntPtr DecContain = Marshal.AllocHGlobal((int)dwBufSize);
        //API.CopyMemory(DecContain, pBuffer, dwBufSize);

        //#define NET_DVR_SYSHEAD     1       系统头数据
        if (dwDataType == HCNetSDK.NET_DVR_SYSHEAD && DecodePort < 0)
        {
            do
            {
                if (!HikPlayer.PlayM4_GetPort(ref DecodePort)) break;

                if (!HikPlayer.PlayM4_SetStreamOpenMode(DecodePort, HikPlayer.STREAME_REALTIME)) break;

                if (!HikPlayer.PlayM4_OpenStream(DecodePort, pBuffer, dwBufSize, 600*1024)) break;

                if (!HikPlayer.PlayM4_GetPictureSize(DecodePort, out pWidth, out pHeight)) break;

                if (!HikPlayer.PlayM4_SetDecCBStream(DecodePort, 1)) break;

                DecCBCallbackHandle = new HCSDK.DecCBFun(DecCallBack);
                if (!HikPlayer.PlayM4_SetDecCallBack(DecodePort, DecCBCallbackHandle)) break;

                if (!HikPlayer.PlayM4_Play(DecodePort, IntPtr.Zero)) break;
            } while (false);
        } //#define NET_DVR_STREAMDATA  2       流数据/视频数据
        else if (dwDataType == HCNetSDK.NET_DVR_STREAMDATA)
        {
            HikPlayer.PlayM4_InputData(DecodePort, pBuffer, dwBufSize);
        }
    }
    /// <summary>
    /// 关闭预览
    /// </summary>
    /// <returns></returns>
    public bool DvcCloseRealPlay()
    {
        if (DvcUserID < 0 || DvcPlayHandle < 0)
            throw new InvalidOperationException();

        OnClose();

        bool Result = HCSDK.HCNetSDK.NET_DVR_StopRealPlay(DvcPlayHandle);

        this.isRealPlaying = false;
        if (Result)
        {
            DvcPlayHandle = -1;
            this.videoChannel = -1;
            //  if (DecodePort >= 0)
            //      ReleaseDecodePort();

        }
        return Result;
    }

    protected virtual void OnClose()
    {

    }
    #endregion

    #region Record

    private const string RecordExtension = "mp4";
    private bool isRecording = false;

    public bool IsRecording
    {
        get { return isRecording; }
    }
    /// <summary>
    /// 开始录像
    /// </summary>
    /// <param name="FileName">录像保存的文件路径</param>
    /// <returns></returns>
    public bool StartRecord(string FileName)
    {
        if (DvcUserID < 0 || DvcPlayHandle < 0 || isRecording)
            throw new InvalidOperationException();

        System.IO.FileInfo FI = new System.IO.FileInfo(FileName);
        string RealFileName = FI.FullName;
        if (FI.Extension.ToLower() != RecordExtension)
            RealFileName = RealFileName + "." + RecordExtension;
        FI = null;

        return isRecording = HCSDK.HCNetSDK.NET_DVR_SaveRealData(DvcPlayHandle, RealFileName);
    }
    /// <summary>
    /// 停止录像
    /// </summary>
    /// <returns></returns>
    public bool StopRecord()
    {
        if (DvcUserID < 0 || DvcPlayHandle < 0 || !isRecording)
            throw new InvalidOperationException();

        if (HCSDK.HCNetSDK.NET_DVR_StopSaveRealData(DvcPlayHandle))
        {
            isRecording = false;
            return true;
        }
        else
            return false;
    }

    #endregion

    #region Snap
    #region Enum

    public enum ImageSizeEnum : ushort
    {
        /// <summary>
        /// CIF (352 * 288)
        /// </summary>
        CIF = 0,

        /// <summary>
        /// QCIF (176 * 144)
        /// </summary>
        QCIF = 1,

        /// <summary>
        /// 480I (720 * 480) / 4CIF (704 * 576)
        /// </summary>
        D1 = 2,

        /// <summary>
        /// UXGA (1600 * 1200), HD ONLY
        /// </summary>
        UXGA = 3,

        /// <summary>
        /// SVGA (800 * 600), IPCAM/HD ONLY
        /// </summary>
        SVGA = 4,

        /// <summary>
        /// HD720p (1280 * 720), HD ONLY
        /// </summary>
        HD720p = 5,

        /// <summary>
        /// VGA (640 * 480), IPCAM/HD ONLY
        /// </summary>
        VGA = 6,

        /// <summary>
        /// XVGA/XGA (1024 * 768), IPCAM/HD ONLY
        /// </summary>
        XVGA = 7,

        /// <summary>
        /// HD900P (1600 * 912), IPCAM/HD ONLY
        /// </summary>
        HD900p = 8
    };

    public enum ImageQualityEnum : ushort
    {
        Standard = 2,
        Good = 1,
        Best = 0
    };

    public enum ImageTypeEnum : ushort
    {
        Bitmap = 0,
        Jpeg = 1
    };

    #endregion


    /// <summary>
    /// 抓拍图像，图像大小默认为CIF,类型为JPG,质量为标准
    /// </summary>
    /// <param name="FileName">图像保存的文件名</param>
    /// <returns></returns>
    public bool SnapsortFromDevice(string FileName)
    {
        return SnapsortFromDevice(FileName, ImageTypeEnum.Jpeg, ImageSizeEnum.CIF, ImageQualityEnum.Standard);
    }
    /// <summary>
    /// 抓拍图像
    /// </summary>
    /// <param name="FileName">图片保存的路径</param>
    /// <param name="ImageType">保存图像的类型</param>
    /// <param name="ImageSize">保存图像的大小</param>
    /// <param name="ImageQuality">图像的质量</param>
    /// <returns>true表示成功，false表示失败</returns>
    public bool SnapsortFromDevice(string FileName, ImageTypeEnum ImageType, ImageSizeEnum ImageSize, ImageQualityEnum ImageQuality)
    {
        if (!InitializedForDevice)
            throw new InvalidOperationException();

        string RealFileName = new System.IO.FileInfo(FileName).FullName;
       
        switch (ImageType)
        {
            case ImageTypeEnum.Bitmap:
                if (DvcPlayHandle < 0)
                    throw new InvalidOperationException();

                return HCSDK.HCNetSDK.NET_DVR_CapturePicture(DvcPlayHandle, RealFileName+".bmp");

            case ImageTypeEnum.Jpeg:
                if (DvcUserID < 0)
                    throw new InvalidOperationException();

                HCSDK.HCNetSDK.NET_DVR_JPEGPARA JPEGPARA = new HCSDK.HCNetSDK.NET_DVR_JPEGPARA();
                JPEGPARA.wPicSize = (ushort)ImageSize;
                JPEGPARA.wPicQuality = (ushort)ImageQuality;

                return HCSDK.HCNetSDK.NET_DVR_CaptureJPEGPicture(DvcUserID, VideoChannel, JPEGPARA, RealFileName+".jpg");

            default:
                throw new ArgumentException();
        }
    }




    #endregion

    #region Control

    /// <summary>
    /// 云台控制命令
    /// </summary>
    public enum PTZCommandEnum : uint
    {
        /// <summary>
        /// 接通灯光电源
        /// </summary>
        LIGHT_PWRON = 2,
        /// <summary>
        /// 接通雨刷开关
        /// </summary>
        WIPER_PWRON = 3,
        /// <summary>
        /// 接通风扇开关
        /// </summary>
        FAN_PWRON = 4,
        /// <summary>
        /// 接通加热器开关
        /// </summary>
        HEATER_PWRON = 5,
        /// <summary>
        /// 接通辅助设备开关
        /// </summary>
        AUX_PWRON1 = 6,
        /// <summary>
        /// 接通辅助设备开关
        /// </summary>
        AUX_PWRON2 = 7,

        /// <summary>
        /// 焦距变大(倍率变大)
        /// </summary>
        ZOOM_IN = 11,
        /// <summary>
        /// 焦距变小(倍率变小)
        /// </summary>
        ZOOM_OUT = 12,
        /// <summary>
        /// 焦点前调
        /// </summary>
        FOCUS_NEAR = 13,
        /// <summary>
        /// 焦点后调
        /// </summary>
        FOCUS_FAR = 14,
        /// <summary>
        /// 光圈扩大
        /// </summary>
        IRIS_OPEN = 15,
        /// <summary>
        /// 光圈缩小
        /// </summary>
        IRIS_CLOSE = 16,

        /// <summary>
        /// 云台上仰
        /// </summary>
        TILT_UP = 21,
        /// <summary>
        /// 云台下俯
        /// </summary>
        TILT_DOWN = 22,
        /// <summary>
        /// 云台左转
        /// </summary>
        PAN_LEFT = 23,
        /// <summary>
        /// 云台右转
        /// </summary>
        PAN_RIGHT = 24,
        /// <summary>
        /// 云台上仰和左转
        /// </summary>
        UP_LEFT = 25,
        /// <summary>
        /// 云台上仰和右转
        /// </summary>
        UP_RIGHT = 26,
        /// <summary>
        /// 云台下俯和左转
        /// </summary>
        DOWN_LEFT = 27,
        /// <summary>
        /// 云台下俯和右转
        /// </summary>
        DOWN_RIGHT = 28,
        /// <summary>
        /// 云台左右自动扫描
        /// </summary>
        PAN_AUTO = 29

        //Other = 0
    };

    #region Direct Method
    /// <summary>
    /// 控制云台运动
    /// </summary>
    /// <param name="Channel">通道号</param>
    /// <param name="Cmd">控制云台运动的命令</param>
    /// <param name="Stop">true表示让云台停止运动，false让云台开始运动</param>
    /// <returns>true表示调用成功</returns>
    public bool PTZControlDirect(int Channel, PTZCommandEnum Cmd, bool Stop)
    {
        if (DvcUserID < 0)
            throw new InvalidOperationException();

        if (Channel < 0 || Channel > DvcInfo.byChanNum)
            throw new ArgumentOutOfRangeException();

        return HCNetSDK.NET_DVR_PTZControl_Other(DvcUserID, Channel, (uint)Cmd, (Stop ? (uint)1 : (uint)0));
    }
    /// <summary>
    /// 控制云台运动
    /// </summary>
    /// <param name="Channel">通道号</param>
    /// <param name="Cmd">控制云台运动的命令</param>
    /// <param name="Stop">true表示让云台停止运动，false让云台开始运动</param>
    /// <param name="Speed">云台运动的速度</param>
    /// <returns>true表示调用成功</returns>

    public bool PTZControlDirect(int Channel, PTZCommandEnum Cmd, bool Stop, byte Speed)
    {
        if (DvcUserID < 0)
            throw new InvalidOperationException();

        if (Channel < 0 || Channel > DvcInfo.byChanNum)
            throw new ArgumentOutOfRangeException();

        if (Speed < 1 || Speed > 7)
            throw new ArgumentOutOfRangeException();

        return HCNetSDK.NET_DVR_PTZControlWithSpeed_Other(DvcUserID, Channel, (uint)Cmd, (Stop ? (uint)1 : (uint)0), Speed);
    }

    public bool PTZControlDirect(PTZCommandEnum Cmd, bool Stop)
    {
        if (VideoChannel < 0)
            throw new InvalidOperationException();

        return PTZControlDirect(VideoChannel, Cmd, Stop);
    }

    public bool PTZControlDirect(PTZCommandEnum Cmd, bool Stop, byte Speed)
    {
        if (VideoChannel < 0)
            throw new InvalidOperationException();

        return PTZControlDirect(VideoChannel, Cmd, Stop, Speed);
    }

    #endregion

    #endregion

    #region Function Delegate

    public delegate void SerialReceiveCallback(byte[] Message);

    #endregion

    #region RS485 Transparent Transfer

    private HCNetSDK.SerialDataCallBack Serial485DataHandle;
    private SerialReceiveCallback Serial485ReceiveHandle;
    private int Serial485Handle = -1;
    public bool EstablishRouteway485(SerialReceiveCallback SerialReceiveCall)
    {
        if (DvcUserID < 0 || Serial485Handle >= 0)
            throw new InvalidOperationException();

        Serial485DataHandle = new HCNetSDK.SerialDataCallBack(Serial485DataCall);

        Serial485Handle = HCNetSDK.NET_DVR_SerialStart(DvcUserID, 2, Serial485DataHandle, 0);

        Serial485ReceiveHandle = (Serial485Handle >= 0) ? SerialReceiveCall : null;

        return (Serial485Handle >= 0);
    }

    private AutoResetEvent SendToRouteway485Lock = new AutoResetEvent(true);
    public bool SendToRouteway485(int Channel, byte[] Message)
    {
        if (Serial485Handle < 0)
            throw new InvalidOperationException();

        if (Channel <= 0)
            throw new ArgumentException();

        if (Message == null || Message.Length == 0)
            throw new ArgumentNullException();

        SendToRouteway485Lock.WaitOne();

        int DataLength = -1;
        IntPtr DataPtr = IntPtr.Zero;

        lock (Message)
        {
            DataLength = Message.Length;
            DataPtr = Marshal.AllocHGlobal(DataLength);
            Marshal.Copy(Message, 0, DataPtr, DataLength);
        }

        bool Result = HCNetSDK.NET_DVR_SerialSend(Serial485Handle, Channel, DataPtr, Convert.ToUInt32(DataLength));

        Marshal.FreeHGlobal(DataPtr);

        SendToRouteway485Lock.Set();

        return Result;
    }

    public bool ShutdownRouteway485()
    {
        if (Serial485Handle < 0)
            throw new InvalidOperationException();

        bool Result = HCNetSDK.NET_DVR_SerialStop(Serial485Handle);

        if (Result)
        {
            Serial485Handle = -1;
            Serial485ReceiveHandle = null;
            Serial485DataHandle = null;
        }

        return Result;
    }

    private void Serial485DataCall(int lSerialHandle, IntPtr pRecvDataBuffer, uint dwBufSize, uint dwUser)
    {
        if (Serial485Handle < 0 || lSerialHandle < 0)
            return;

        if (Serial485Handle != lSerialHandle)
            return;

        byte[] Message = new byte[dwBufSize];
        Marshal.Copy(pRecvDataBuffer, Message, 0, Convert.ToInt32(dwBufSize));

        if (Serial485ReceiveHandle != null)
            this.BeginInvoke(Serial485ReceiveHandle, new object[] { Message });
    }

    #endregion

    #region Decode
    private HCSDK.DecCBFun DecCBCallbackHandle = null;
   
    public event EventHandler<EventArgsStreamVideo> StreamVideo=null;
    public class EventArgsStreamVideo : EventArgs
    {
        public IntPtr Buffer { get; private set; }
        public int Width { get; private set; }
        public int Height { get; private set; }
        public EventArgsStreamVideo(IntPtr pBuf, int w, int h)
        {
            this.Buffer = pBuf;
            this.Width = w;
            this.Height = h;
        }
    }
    protected virtual void OnStreamVideo(IntPtr pBuf, int w, int h)
    {
    }
    private void DecCallBack(int nPort, IntPtr pBuf, int nSize, ref FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
    {
        int W = pFrameInfo.nWidth;
        int H = pFrameInfo.nHeight;
       // byte[] DecBuffer = new byte[nSize];
      //  Marshal.Copy(pBuf, DecBuffer, 0, nSize);        
        if (StreamVideo != null) StreamVideo(this, new EventArgsStreamVideo(pBuf, W, H));
        this.OnStreamVideo(pBuf, W, H);
      //  Marshal.FreeHGlobal(pBuf);

    }
    #endregion
}
}
